﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;


namespace Nachos.Machine
{
    public partial class Machine
    {
        private TranslationEntry tlb;		// this pointer should be considered 
        // "read-only" to Nachos kernel code

        private TranslationEntry[] pageTable;
        private int[] registers;
        private byte[] mainMemory;

        public const int PageSize = 128; 		// set the page size equal to
        // the disk sector size, for simplicity
        public const int NumberOfTotalRegisters = 40;
        public const int NumberOfPhysicalPages = 32;
        public const int MemorySize = (NumberOfPhysicalPages * PageSize);
        public const int TLBSize = 4;			// if there is a TLB, make it small
        public uint pageTableSize;

        public bool UsingTlb { get; set; }

        public Machine(bool debug)
        {
            int i;
            registers = new int[NumberOfTotalRegisters];
            for (i = 0; i < NumberOfTotalRegisters; i++)
            {
                registers[i] = 0;
            }
            mainMemory = new byte[MemorySize];
            for (i = 0; i < MemorySize; i++)
            {
                mainMemory[i] = 0;
            }
            if (UsingTlb)
            {
                tlb = new TranslationEntry[TLBSize];
                for (i = 0; i < TLBSize; i++)
                {
                    tlb[i].Valid = false;
                }

                pageTable = null;
            }	// use linear page table
            else
            {   tlb = null;
                pageTable = null;
            }

            singleStep = debug;
        }

        public int ReadRegister(int num)
        {
            //ASSERT((num >= 0) && (num < NumTotalRegs));
            return registers[num];
        }

        public void WriteRegister(int num, int value)
        {
            //ASSERT((num >= 0) && (num < NumTotalRegs));
            registers[num] = value;
        }

        /// <summary>
        /// Transfer control to the Nachos kernel from user mode, because
        ///	the user program either invoked a system call, or some exception
        ///	occured (such as the address translation failed).
        /// </summary>
        /// <param name="which">the cause of the kernel trap</param>
        /// <param name="badVAddr">the virtual address causing the trap, if appropriate</param>
        public void RaiseException(ExceptionType which, int badVAddr)
        {
            //DEBUG(dbgMach, "Exception: " << exceptionNames[which]);
    
            registers[BadVAddrReg] = badVAddr;
            DelayedLoad(0, 0);			// finish anything in progress
            kernel.Interrupt.SetStatus(MachineStatus.SystemMode);
            ExceptionHandler(which);		// interrupts are enabled at this point
            kernel.Interrupt.SetStatus(MachineStatus.UserMode);
        }


        void Debugger()
        {
            string buf;
            int num;

            kernel.Interrupt.DumpState();
            DumpState();
            //cout << kernel->stats->totalTicks << ">";
            buf = Console.ReadLine();
            num = Int32.Parse(buf.Substring(0,1));
            if (num == 1)
            {
                runUntilTime = num;
            }
            else 
            {
                runUntilTime = 0;
            
                switch (buf) 
                {
                    case "\n":
                    break;
        
                    case "c":
                        singleStep = false;
                    break;
        
                    case "?":
                    //cout << "Machine commands:\n";
                    //cout << "    <return>  execute one instruction\n";
                    //cout << "    <number>  run until the given timer tick\n";
                    //cout << "    c         run until completion\n";
                    //cout << "    ?         print help message\n";
                    break;
                }
            }
        }

        void DumpState()
        {
            int i;
    
            //cout << "Machine registers:\n";
            for (i = 0; i < NumGPRegs; i++) 
            {
                switch (i) {
                  case StackReg:
                    //cout << "\tSP(" << i << "):\t" << registers[i];
                    break;
        
                  case RetAddrReg:
                    //cout << "\tRA(" << i << "):\t" << registers[i];
                    break;
      
                  default:
                    //cout << "\t" << i << ":\t" << registers[i];
                    break;
                }
                if ((i % 4) == 3) { /*cout << "\n";*/ }
            }
    
            /*cout << "\tHi:\t" << registers[HiReg];
            cout << "\tLo:\t" << registers[LoReg];
            cout << "\tPC:\t" << registers[PCReg];
            cout << "\tNextPC:\t" << registers[NextPCReg];
            cout << "\tPrevPC:\t" << registers[PrevPCReg];
            cout << "\tLoad:\t" << registers[LoadReg];
            cout << "\tLoadV:\t" << registers[LoadValueReg] << "\n";*/
        }
    }
}
